package com.automannn.cas.practice.springWebflow;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.BeansException;
import org.springframework.binding.convert.service.DefaultConversionService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.Primary;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.UrlFilenameViewController;
import org.springframework.web.servlet.view.AbstractTemplateView;
import org.springframework.web.servlet.view.AbstractView;
import org.springframework.web.servlet.view.InternalResourceView;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.config.FlowBuilderServicesBuilder;
import org.springframework.webflow.config.FlowDefinitionRegistryBuilder;
import org.springframework.webflow.conversation.ConversationManager;
import org.springframework.webflow.conversation.impl.SessionBindingConversationManager;
import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistryImpl;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.FlowExecutionFactory;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.repository.FlowExecutionRepository;
import org.springframework.webflow.execution.repository.impl.DefaultFlowExecutionRepository;
import org.springframework.webflow.execution.repository.snapshot.FlowExecutionSnapshotFactory;
import org.springframework.webflow.execution.repository.snapshot.SimpleFlowExecutionSnapshotFactory;
import org.springframework.webflow.executor.FlowExecutor;
import org.springframework.webflow.executor.FlowExecutorImpl;
import org.springframework.webflow.mvc.builder.MvcViewFactoryCreator;
import org.springframework.webflow.mvc.servlet.FlowHandlerAdapter;
import org.springframework.webflow.mvc.servlet.FlowHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * @author chenkh
 * @time 2021/11/19 11:19
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@RestController
/*xxx: 可以简单粗暴的理解为:
*   同一个请求地址，通过 _eventId进行流程流转，显示不同的页面,除了事件必传外，流程节点的id也必须带，才能够进行正确的流程流转
* */
public class SpringWebFlowTestWithSpringBoot implements ApplicationContextAware {

    public static void main(String[] args) {
        SpringApplication.run(SpringWebFlowTestWithSpringBoot.class);
    }

    private ApplicationContext applicationContext;

    @RequestMapping("hi")
    public String sayHi(){
      return "hello,world!";
    };


    @Bean
    public HandlerMapping flowHandlerMapping(FlowDefinitionRegistry flowDefinitionRegistry){
        FlowHandlerMapping flowHandlerMapping = new MyHandlerMapping();
        flowHandlerMapping.setFlowRegistry(flowDefinitionRegistry);

        //根据请求的路径映射视图
        UrlFilenameViewController handler = new UrlFilenameViewController();
        flowHandlerMapping.setDefaultHandler(handler);
        return flowHandlerMapping;
    }

    public static class MyHandlerMapping extends FlowHandlerMapping implements PriorityOrdered{

    }

    @Bean
    public HandlerAdapter flowHandlerAdapter(FlowExecutor executor){
        FlowHandlerAdapter flowHandlerAdapter = new FlowHandlerAdapter();
        flowHandlerAdapter.setFlowExecutor(executor);
        return flowHandlerAdapter;
    }

    @Bean
    public ViewResolver internalResourceViewResolver(){
        InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
        internalResourceViewResolver.setPrefix("/views/");
        internalResourceViewResolver.setSuffix(".html");
        internalResourceViewResolver.setViewClass(AutomannnView.class);
        internalResourceViewResolver.setApplicationContext(this.applicationContext);
        return internalResourceViewResolver;
    }

    @Bean
    public FlowBuilderServices flowBuilderServices(List<ViewResolver> viewResolvers){
        FlowBuilderServicesBuilder flowBuilderServicesBuilder= new FlowBuilderServicesBuilder();
        MvcViewFactoryCreator mvcViewFactoryCreator = new MvcViewFactoryCreator();

        mvcViewFactoryCreator.setViewResolvers(viewResolvers);
        flowBuilderServicesBuilder.setViewFactoryCreator(mvcViewFactoryCreator);
        flowBuilderServicesBuilder.setConversionService(new DefaultConversionService());
        return flowBuilderServicesBuilder.build();
    };

    @Bean
    public FlowDefinitionRegistry flowDefinitionRegistry(FlowBuilderServices flowBuilderServices){
        FlowDefinitionRegistryBuilder builder =new FlowDefinitionRegistryBuilder(this.applicationContext);
        builder.addFlowLocation("classpath:webflow/test.xml","doSomeThing");
        builder.setFlowBuilderServices(flowBuilderServices);

        return builder.build();
    };

    public static class AutomannnView extends InternalResourceView {

        protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception{
            this.renderMergedTemplateModel(model,request,response);
        }

        private void renderMergedTemplateModel(Map<String, Object> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            //根据url,加载并渲染
            String url = this.getUrl();
            Assert.state(url != null, "'url' not set");
            Resource resource = new ClassPathResource(url);
            System.out.println("model参数为:");
            System.out.println(map);
            if(resource.exists()){
                InputStream inputStream= resource.getInputStream();
                OutputStream outputStream= httpServletResponse.getOutputStream();
                IOUtils.copy(inputStream,outputStream);
                outputStream.flush();
            }else{
                httpServletResponse.setStatus(404);
            }
        }

        @Override
        public String getContentType() {
            return "text/html;";
        }
    }

    @Bean
    public FlowExecutor flowExecutor(FlowDefinitionLocator locator){
        FlowExecutionImplFactory factory = new FlowExecutionImplFactory();

        ConversationManager manager = new SessionBindingConversationManager();
        FlowExecutionSnapshotFactory snapshotFactory = new SimpleFlowExecutionSnapshotFactory(factory,locator);

        DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(manager,snapshotFactory);

        //必须设置key生成器
        factory.setExecutionKeyFactory(repository);

        FlowExecutor flowExecutor=new FlowExecutorImpl(locator,factory,repository);
        return flowExecutor;
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Component("automannnInitFlow")
    public static class AutomannnInitFlow extends AbstractAction{

        @Override
        protected Event doExecute(RequestContext context) throws Exception {
            System.out.println("automannn,全局初始化");
            return success();
        }
    }

    @Component("automannnBusinessAction")
    public static class AutomannnBusinessAction extends AbstractAction{

        @Override
        protected Event doExecute(RequestContext context) throws Exception {
            System.out.println("执行业务接口，随机成功或者失败");
            Random random= new Random();
            if(random.nextBoolean()){
                return success();
            }else{
                return new Event(this,"customEvent");
            }
        }
    }
}
